Skip to content

Develop to main#90

Merged
mateof merged 4 commits into
mainfrom
develop
Apr 14, 2026
Merged

Develop to main#90
mateof merged 4 commits into
mainfrom
develop

Conversation

@mateof

@mateof mateof commented Apr 14, 2026

Copy link
Copy Markdown
Owner

No description provided.

@mateof
mateof merged commit 828edca into main Apr 14, 2026
1 check passed
mateof added a commit that referenced this pull request Apr 14, 2026
* Develop to main (#90)

* fix: fix folder creation logic to ensure CurrentFolder is valid before proceeding

* fix: solve upload to empty folder and upload to root folder

* fix: solve folder problem

* Bump version to 3.6.3

---------

Co-authored-by: Mateo <mateof@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
mateof added a commit that referenced this pull request Jul 22, 2026
* fix: fix folder creation logic to ensure CurrentFolder is valid before proceeding

* fix: solve upload to empty folder and upload to root folder

* fix: solve folder problem

* Sync develop with main after v3.6.3 (#93)

* Develop to main (#90)

* fix: fix folder creation logic to ensure CurrentFolder is valid before proceeding

* fix: solve upload to empty folder and upload to root folder

* fix: solve folder problem

* Bump version to 3.6.3

---------

Co-authored-by: Mateo <mateof@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* fix: update MongoDB.Driver and System.IO.Hashing package versions

* feat: wire progressive download cache into tfm streaming endpoint

- StreamAudioByTfmId now starts the background ProgressiveDownloadService
  download (previously injected but never invoked) so every streamed track
  is fetched from Telegram once and persisted to the disk cache
- Serve ranges from the growing cache file, waiting briefly when the
  background download is close instead of opening duplicate fetches
- Direct Telegram fetches (far seeks) now stream 512KB chunks to the
  response via new DownloadFileStreamChunks and are limited by a semaphore
- Robust Range parsing: TryParse, suffix ranges (bytes=-N), 416 for
  unsatisfiable ranges, removed ambiguous to==0 sentinel
- ProgressiveDownloadService: fix IsRangeAvailable null check, drop stale
  entries when cache files are evicted, cap retries with backoff, and trim
  the cache directory to 10GB (oldest first, throttled)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat: configurable STRM streaming mode with progressive disk cache

Add a 'Streaming Mode' setting (Direct streaming / Progressive cache /
Full preload) selectable in the web Config page, used when generating
STRM files for Emby/Kodi.

- New GetFileStreamCached endpoint: streams from Telegram with Range
  support while a background download fills the local cache; later
  ranges and replays are served from disk.
- ProgressiveDownloadService now supports split (multi-message) files,
  caching parts sequentially into a single file with resume support.
- Direct seeks ahead of the cache locate the Telegram part containing
  the requested range and cap the response at the part boundary.
- STRM generation picks the endpoint from the configured mode; files
  smaller than MaxPreloadFileSizeInMb are still fully preloaded.
- Backwards compatible with the legacy PreloadFilesOnStream flag (kept
  in sync on save; used as fallback when the new setting is unset).

* fix: show progress and honor cancel for progressive cache downloads

The background cache download appeared in the downloads list but never
updated its progress and could not be stopped:

- Report progress through DownloadModel.ProgressCallback (updates
  percentage, transmitted/size strings, global speed stats and fires
  the UI events) instead of writing _transmitted directly. The task is
  now also marked Completed when the download finishes.
- Check the task state on every chunk so pressing Cancel (or Pause)
  in the downloads list actually stops the background download.
- A user cancel is remembered for 1 hour per file so ongoing playback
  range requests don't immediately restart the cache download; the
  stream keeps working through direct Telegram fetches. A later
  playback caches again.
- Start the progress counter at the resume offset so resumed downloads
  don't inflate the global speed stats.
- Incomplete downloads no longer linger as 'Working' in the list.

* feat: audio transcoding endpoint for offline downloads (MP3/AAC)

- GET api/mobile/stream/tfm/{channelId}/{tfmId}/transcoded?format=mp3|aac&bitrate=N
  downloads the original into the streaming cache if needed, transcodes it
  with FFmpeg and serves the result with Range support; transcodes are
  cached on disk under _temp/transcoded so repeat requests are instant
- GET api/mobile/stream/transcode/info reports FFmpeg availability so
  clients can warn the user and fall back to original downloads
- Returns 501 when FFmpeg is missing; per-target locks avoid duplicate
  transcodes and a semaphore caps concurrent FFmpeg processes at 2
- MP3 keeps embedded cover art and tags; AAC (m4a) keeps tags

* fix: reliable live task updates in Tasks Manager

Task, download and upload tables required a full page reload to show
progress because per-model event subscriptions were only wired when the
component's static cached list was empty, so newly created models never
got subscribed. Static lists were also shared between the Active and
Queue table instances, and grid refreshes were fired from background
threads without dispatcher marshaling.

TransactionInfoService now owns all per-model subscriptions (hooked when
a model enters any list, unhooked on remove/clear) and exposes a single
aggregated TransactionsChanged event, throttled to 250ms with a
guaranteed trailing raise. Tables use instance lists and subscribe only
to that event, refreshing via InvokeAsync. The Tasks Manager header
stats now update live as well.

* feat: configurable parallel chunk transfers for faster downloads/uploads

Transfers were capped at ~5-7 MB/s because WTelegramClient requests
512KB file parts with only 2 requests in flight (its default since
v4.x), making throughput latency-bound at roughly 1MB per round-trip
to Telegram's data center.

Add a ParallelTransfers setting (default 4, range 1-16) applied to the
main client and, before each document download, to the media-DC client
that WTelegram resolves internally - secondary DC clients do not
inherit the main client's setting, so it must be propagated per
instance. The applied value is tracked per client and adjusted by
delta, which stays correct even while parts are in flight. Changes
take effect on the next transfer without restarting.

* feat: multi-connection downloads to bypass per-connection speed limit

Telegram enforces its throughput limit per MTProto connection (~5-6
MB/s), so pipelining more chunks on one connection cannot go faster;
pushing harder only triggers FLOOD_PREMIUM_WAIT penalties. Official
clients reach 50+ MB/s by opening several sessions to the file DC and
splitting the file between them.

Add an experimental multi-connection download mode (off by default):
a per-DC pool of extra authorized clients, bootstrapped once from the
main client via auth.exportAuthorization/importAuthorization, persisted
as session files and reused across restarts. Files >=32MB are split in
4MB blocks served concurrently by 2-8 connections (configurable), each
part written at its absolute offset via RandomAccess. Progress reports
the contiguous completed prefix so persistence keeps a safe resume
offset, while speed accounting counts every received part. Any failure
falls back transparently to the standard sequential download.

* fix: enable multi-connection mode on the file-manager download path

Multi-connection downloads were only hooked into DownloadFile, but
file-manager download tasks go through DownloadFileNow, which calls
DownloadFileAndReturn with a FileStream target, so the feature never
triggered on the most common path. Hook DownloadFileAndReturn (and the
offset==0 branch of DownloadFileAndReturnWithOffset) when the
destination is a FileStream, which supports the positional writes
multi-connection mode requires. Memory and non-seekable targets keep
the sequential path.

* fix: bootstrap download pool via neighbor DC to avoid DC_ID_INVALID

Telegram rejects auth.exportAuthorization towards the DC the caller is
already connected to, so the pool bootstrap failed with DC_ID_INVALID
whenever the file lived on the account home DC (the common case) and
downloads always fell back to a single connection.

Home the pool clients on a neighbor DC instead: exporting from the main
client to the neighbor is always cross-DC, and from there the pool
client can export towards any file DC, including the account home DC -
both hops are cross-DC and accepted. This also makes the pool global
rather than per-DC (one set of sessions serves every DC through
per-file-DC transfer clients resolved with GetClientForDC), and the
bootstrap failure log now includes the error message.

* fix: finalize pool client login and drop probe RPCs after import

The pool bootstrap verified the imported authorization with
users.getUsers, which answers AUTH_KEY_UNREGISTERED on an imported
session even though the import succeeded and file requests work, so the
bootstrap always aborted and downloads fell back to a single
connection.

Import the exported authorization as the first call on the fresh
client, finalize it client-side with LoginAlreadyDone (recording the
UserId in the session), and make no verification RPC - workers verify
by use and fall back on error. With the UserId recorded, WTelegram's
GetClientForDC now handles the per-file-DC authorization automatically,
replacing the manual transfer-client export/import logic; persisted
sessions skip the import on later runs via the recorded UserId.

* fix: tolerate re-import on an already-authorized pool session key

Session files persisted by earlier runs (where the import succeeded but
the login was never finalized client-side) hold a key that already
carries the account authorization; re-importing onto it is rejected by
the server with AUTH_BYTES_INVALID and the bootstrap aborted.

Treat AUTH_BYTES_INVALID on import as already-authorized: finalize the
login client-side with the user id returned by exportAuthorization and
let the workers verify the session by use, falling back to the
sequential download if it turns out to be broken.

* feat: pool clients clone the main session instead of importing auth

The exportAuthorization/importAuthorization bootstrap turned out to be
a dead end: an imported session is a limited authorization - file
requests work, but probe RPCs and re-exports answer
AUTH_KEY_UNREGISTERED - so the pool client could never authorize its
per-file-DC transfer connections (GetClientForDC's automatic export
failed with AUTH_KEY_UNREGISTERED) and downloads always fell back.

Clone the main session file instead: each pool client loads a copy of
WTelegram.session, sharing the fully-authorized main auth key, while
WTelegram assigns a fresh transient MTProto session id per client
instance. The server sees extra connections of the existing
authorization - the exact model official clients use for parallel
downloads. Telegram's throughput limit is per connection/session, not
per auth key, so each clone gets its own allowance. No authorizations
are created, nothing appears in the device list, and cross-DC files
keep working because the clones are fully logged sessions.

* fix: clone the session despite the live file lock

The main client keeps WTelegram.session open (often exclusively), so
File.Copy failed with a sharing violation and the pool bootstrap always
fell back to single-connection downloads.

Snapshot the session file at client creation, before WTelegram opens
and locks it, and make the clone routine try a share-friendly stream
copy of the live file first, falling back to the startup snapshot.
Session state staleness is harmless: the auth key never changes and
server salts are renegotiated automatically.

* feat: pipeline file parts within each download connection (#107)

* feat: pipeline file parts within each download connection

Workers requested their block's 1MB parts sequentially, paying a full
round-trip of dead time per part and capping each connection around
3-4 MB/s regardless of its server-side allowance - the multi-connection
download barely improved on a single connection (~10 MB/s with dips).

Request all parts of a block concurrently on the block's connection,
keeping each connection's pipe full (the same pipelining official
clients use), and log a completion summary with the effective average
speed to make future tuning measurable. Part writes stay positional
and block completion still gates the contiguous confirmed prefix used
for progress and resume persistence.

* feat: make multi-connection tuning configurable with documented defaults

Expose the remaining hardcoded transfer knobs in General Config: chunk
size (snapped to Telegram's allowed 128/256/512/1024 KB values, 512
being WTelegramClient's own default), block size per connection (which
determines the requests in flight per connection) and the minimum file
size for multi-connection mode. Values are captured once per download
so a config change cannot desynchronize offsets mid-transfer.

The Config page now states the library default, app default and
recommended value for each transfer setting, so the stock WTelegramClient
behavior (2 parallel chunks, 512KB parts, single connection) can be
restored by configuration alone.

* feat: real sea-wave effect on the top bar download/upload buttons

The water-fill buttons showed a flat colored block with a horizontal
shine sweep - no actual wave at the waterline. Replace the shine with
two repeating SVG wave crests that ride exactly on the fill level (the
.water-wave element's top edge), drifting horizontally at different
speeds and in opposite directions, overlapping the fill by 1px so no
seam shows. The fill itself now uses a vertical gradient (deeper at the
bottom, lighter at the surface) per variant color, wave motion is
disabled under prefers-reduced-motion, and the stylesheet link gets a
version query so cached browsers pick up the change. No markup changes
to the buttons themselves.

* feat: auth guard before rendering with return to the requested page

After an app restart the Telegram client exists but the user is not
loaded, so pages rendered first and only then (on first render) the
layout noticed the missing user and hard-redirected to the login page,
losing the URL the user was visiting; after authenticating, login
always landed on /fetchdata.

Resolve the session in the layouts' OnInitializedAsync, before any page
content renders: the body is gated behind the check (page components
are not instantiated until it completes), a silent session restore via
checkAuth(null) is attempted first, and only when interactive login is
really needed does the guard bounce to the login page, passing the
requested URL as ?returnUrl. The login page navigates back to that URL
(local paths only, to avoid open redirects) after a successful login.
ConfigLayout gets the same guard; the old after-render check is
removed.

* feat: QR code login option in the web login page

The QR login plumbing (CallQrGenerator wrapping LoginWithQRCode, QR
rendering in Index) existed but nothing invoked it, and it could not
have worked from the web anyway: when the account has 2FA,
LoginWithQRCode asks for the password through Config("password"), which
with the convenience constructor prompts on the console - QR login only
worked when driving the library from a terminal.

Wire it end to end:
- The main client is now built with a custom config callback that
  routes the "password" request to the web UI: a QrPasswordNeeded event
  switches the login form to the 2FA password step and the value is
  handed back to the waiting login task, which blocks on a
  TaskCompletionSource (5 minute timeout) on its background thread.
- The login page offers "Log in with QR code" next to the phone step:
  it shows the tg://login QR (auto-refreshed by the library when each
  token expires), instructions and a back button; on success the shared
  post-login initialization runs and navigation proceeds as with the
  normal flow.
- DoLogin's post-login tail is extracted into CompleteLogin, reused by
  the QR flow.
- checkAuth: when a session exists but no user data file is saved (the
  QR case - there is no phone to save), attempt a silent session
  restore via DoLogin(null) instead of always bouncing to the phone
  step, so QR sessions survive app restarts.

* feat: live refresh in the download/upload/task info modals

The info modals held a live reference to their model but only rendered
a snapshot taken when opened, so progress, state, duration and speed
froze until the modal was reopened.

Subscribe each modal to the aggregated throttled TransactionsChanged
event while it is visible (subscribed on ShowModal, unsubscribed on
HideModal and Dispose) and re-render on the UI thread, following the
same pattern the transfer tables use.
mateof added a commit that referenced this pull request Jul 22, 2026
* Develop to main (#90)

* fix: fix folder creation logic to ensure CurrentFolder is valid before proceeding

* fix: solve upload to empty folder and upload to root folder

* fix: solve folder problem

* Bump version to 3.6.3

* Develop to main  (#112)

* fix: fix folder creation logic to ensure CurrentFolder is valid before proceeding

* fix: solve upload to empty folder and upload to root folder

* fix: solve folder problem

* Sync develop with main after v3.6.3 (#93)

* Develop to main (#90)

* fix: fix folder creation logic to ensure CurrentFolder is valid before proceeding

* fix: solve upload to empty folder and upload to root folder

* fix: solve folder problem

* Bump version to 3.6.3

---------

Co-authored-by: Mateo <mateof@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* fix: update MongoDB.Driver and System.IO.Hashing package versions

* feat: wire progressive download cache into tfm streaming endpoint

- StreamAudioByTfmId now starts the background ProgressiveDownloadService
  download (previously injected but never invoked) so every streamed track
  is fetched from Telegram once and persisted to the disk cache
- Serve ranges from the growing cache file, waiting briefly when the
  background download is close instead of opening duplicate fetches
- Direct Telegram fetches (far seeks) now stream 512KB chunks to the
  response via new DownloadFileStreamChunks and are limited by a semaphore
- Robust Range parsing: TryParse, suffix ranges (bytes=-N), 416 for
  unsatisfiable ranges, removed ambiguous to==0 sentinel
- ProgressiveDownloadService: fix IsRangeAvailable null check, drop stale
  entries when cache files are evicted, cap retries with backoff, and trim
  the cache directory to 10GB (oldest first, throttled)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat: configurable STRM streaming mode with progressive disk cache

Add a 'Streaming Mode' setting (Direct streaming / Progressive cache /
Full preload) selectable in the web Config page, used when generating
STRM files for Emby/Kodi.

- New GetFileStreamCached endpoint: streams from Telegram with Range
  support while a background download fills the local cache; later
  ranges and replays are served from disk.
- ProgressiveDownloadService now supports split (multi-message) files,
  caching parts sequentially into a single file with resume support.
- Direct seeks ahead of the cache locate the Telegram part containing
  the requested range and cap the response at the part boundary.
- STRM generation picks the endpoint from the configured mode; files
  smaller than MaxPreloadFileSizeInMb are still fully preloaded.
- Backwards compatible with the legacy PreloadFilesOnStream flag (kept
  in sync on save; used as fallback when the new setting is unset).

* fix: show progress and honor cancel for progressive cache downloads

The background cache download appeared in the downloads list but never
updated its progress and could not be stopped:

- Report progress through DownloadModel.ProgressCallback (updates
  percentage, transmitted/size strings, global speed stats and fires
  the UI events) instead of writing _transmitted directly. The task is
  now also marked Completed when the download finishes.
- Check the task state on every chunk so pressing Cancel (or Pause)
  in the downloads list actually stops the background download.
- A user cancel is remembered for 1 hour per file so ongoing playback
  range requests don't immediately restart the cache download; the
  stream keeps working through direct Telegram fetches. A later
  playback caches again.
- Start the progress counter at the resume offset so resumed downloads
  don't inflate the global speed stats.
- Incomplete downloads no longer linger as 'Working' in the list.

* feat: audio transcoding endpoint for offline downloads (MP3/AAC)

- GET api/mobile/stream/tfm/{channelId}/{tfmId}/transcoded?format=mp3|aac&bitrate=N
  downloads the original into the streaming cache if needed, transcodes it
  with FFmpeg and serves the result with Range support; transcodes are
  cached on disk under _temp/transcoded so repeat requests are instant
- GET api/mobile/stream/transcode/info reports FFmpeg availability so
  clients can warn the user and fall back to original downloads
- Returns 501 when FFmpeg is missing; per-target locks avoid duplicate
  transcodes and a semaphore caps concurrent FFmpeg processes at 2
- MP3 keeps embedded cover art and tags; AAC (m4a) keeps tags

* fix: reliable live task updates in Tasks Manager

Task, download and upload tables required a full page reload to show
progress because per-model event subscriptions were only wired when the
component's static cached list was empty, so newly created models never
got subscribed. Static lists were also shared between the Active and
Queue table instances, and grid refreshes were fired from background
threads without dispatcher marshaling.

TransactionInfoService now owns all per-model subscriptions (hooked when
a model enters any list, unhooked on remove/clear) and exposes a single
aggregated TransactionsChanged event, throttled to 250ms with a
guaranteed trailing raise. Tables use instance lists and subscribe only
to that event, refreshing via InvokeAsync. The Tasks Manager header
stats now update live as well.

* feat: configurable parallel chunk transfers for faster downloads/uploads

Transfers were capped at ~5-7 MB/s because WTelegramClient requests
512KB file parts with only 2 requests in flight (its default since
v4.x), making throughput latency-bound at roughly 1MB per round-trip
to Telegram's data center.

Add a ParallelTransfers setting (default 4, range 1-16) applied to the
main client and, before each document download, to the media-DC client
that WTelegram resolves internally - secondary DC clients do not
inherit the main client's setting, so it must be propagated per
instance. The applied value is tracked per client and adjusted by
delta, which stays correct even while parts are in flight. Changes
take effect on the next transfer without restarting.

* feat: multi-connection downloads to bypass per-connection speed limit

Telegram enforces its throughput limit per MTProto connection (~5-6
MB/s), so pipelining more chunks on one connection cannot go faster;
pushing harder only triggers FLOOD_PREMIUM_WAIT penalties. Official
clients reach 50+ MB/s by opening several sessions to the file DC and
splitting the file between them.

Add an experimental multi-connection download mode (off by default):
a per-DC pool of extra authorized clients, bootstrapped once from the
main client via auth.exportAuthorization/importAuthorization, persisted
as session files and reused across restarts. Files >=32MB are split in
4MB blocks served concurrently by 2-8 connections (configurable), each
part written at its absolute offset via RandomAccess. Progress reports
the contiguous completed prefix so persistence keeps a safe resume
offset, while speed accounting counts every received part. Any failure
falls back transparently to the standard sequential download.

* fix: enable multi-connection mode on the file-manager download path

Multi-connection downloads were only hooked into DownloadFile, but
file-manager download tasks go through DownloadFileNow, which calls
DownloadFileAndReturn with a FileStream target, so the feature never
triggered on the most common path. Hook DownloadFileAndReturn (and the
offset==0 branch of DownloadFileAndReturnWithOffset) when the
destination is a FileStream, which supports the positional writes
multi-connection mode requires. Memory and non-seekable targets keep
the sequential path.

* fix: bootstrap download pool via neighbor DC to avoid DC_ID_INVALID

Telegram rejects auth.exportAuthorization towards the DC the caller is
already connected to, so the pool bootstrap failed with DC_ID_INVALID
whenever the file lived on the account home DC (the common case) and
downloads always fell back to a single connection.

Home the pool clients on a neighbor DC instead: exporting from the main
client to the neighbor is always cross-DC, and from there the pool
client can export towards any file DC, including the account home DC -
both hops are cross-DC and accepted. This also makes the pool global
rather than per-DC (one set of sessions serves every DC through
per-file-DC transfer clients resolved with GetClientForDC), and the
bootstrap failure log now includes the error message.

* fix: finalize pool client login and drop probe RPCs after import

The pool bootstrap verified the imported authorization with
users.getUsers, which answers AUTH_KEY_UNREGISTERED on an imported
session even though the import succeeded and file requests work, so the
bootstrap always aborted and downloads fell back to a single
connection.

Import the exported authorization as the first call on the fresh
client, finalize it client-side with LoginAlreadyDone (recording the
UserId in the session), and make no verification RPC - workers verify
by use and fall back on error. With the UserId recorded, WTelegram's
GetClientForDC now handles the per-file-DC authorization automatically,
replacing the manual transfer-client export/import logic; persisted
sessions skip the import on later runs via the recorded UserId.

* fix: tolerate re-import on an already-authorized pool session key

Session files persisted by earlier runs (where the import succeeded but
the login was never finalized client-side) hold a key that already
carries the account authorization; re-importing onto it is rejected by
the server with AUTH_BYTES_INVALID and the bootstrap aborted.

Treat AUTH_BYTES_INVALID on import as already-authorized: finalize the
login client-side with the user id returned by exportAuthorization and
let the workers verify the session by use, falling back to the
sequential download if it turns out to be broken.

* feat: pool clients clone the main session instead of importing auth

The exportAuthorization/importAuthorization bootstrap turned out to be
a dead end: an imported session is a limited authorization - file
requests work, but probe RPCs and re-exports answer
AUTH_KEY_UNREGISTERED - so the pool client could never authorize its
per-file-DC transfer connections (GetClientForDC's automatic export
failed with AUTH_KEY_UNREGISTERED) and downloads always fell back.

Clone the main session file instead: each pool client loads a copy of
WTelegram.session, sharing the fully-authorized main auth key, while
WTelegram assigns a fresh transient MTProto session id per client
instance. The server sees extra connections of the existing
authorization - the exact model official clients use for parallel
downloads. Telegram's throughput limit is per connection/session, not
per auth key, so each clone gets its own allowance. No authorizations
are created, nothing appears in the device list, and cross-DC files
keep working because the clones are fully logged sessions.

* fix: clone the session despite the live file lock

The main client keeps WTelegram.session open (often exclusively), so
File.Copy failed with a sharing violation and the pool bootstrap always
fell back to single-connection downloads.

Snapshot the session file at client creation, before WTelegram opens
and locks it, and make the clone routine try a share-friendly stream
copy of the live file first, falling back to the startup snapshot.
Session state staleness is harmless: the auth key never changes and
server salts are renegotiated automatically.

* feat: pipeline file parts within each download connection (#107)

* feat: pipeline file parts within each download connection

Workers requested their block's 1MB parts sequentially, paying a full
round-trip of dead time per part and capping each connection around
3-4 MB/s regardless of its server-side allowance - the multi-connection
download barely improved on a single connection (~10 MB/s with dips).

Request all parts of a block concurrently on the block's connection,
keeping each connection's pipe full (the same pipelining official
clients use), and log a completion summary with the effective average
speed to make future tuning measurable. Part writes stay positional
and block completion still gates the contiguous confirmed prefix used
for progress and resume persistence.

* feat: make multi-connection tuning configurable with documented defaults

Expose the remaining hardcoded transfer knobs in General Config: chunk
size (snapped to Telegram's allowed 128/256/512/1024 KB values, 512
being WTelegramClient's own default), block size per connection (which
determines the requests in flight per connection) and the minimum file
size for multi-connection mode. Values are captured once per download
so a config change cannot desynchronize offsets mid-transfer.

The Config page now states the library default, app default and
recommended value for each transfer setting, so the stock WTelegramClient
behavior (2 parallel chunks, 512KB parts, single connection) can be
restored by configuration alone.

* feat: real sea-wave effect on the top bar download/upload buttons

The water-fill buttons showed a flat colored block with a horizontal
shine sweep - no actual wave at the waterline. Replace the shine with
two repeating SVG wave crests that ride exactly on the fill level (the
.water-wave element's top edge), drifting horizontally at different
speeds and in opposite directions, overlapping the fill by 1px so no
seam shows. The fill itself now uses a vertical gradient (deeper at the
bottom, lighter at the surface) per variant color, wave motion is
disabled under prefers-reduced-motion, and the stylesheet link gets a
version query so cached browsers pick up the change. No markup changes
to the buttons themselves.

* feat: auth guard before rendering with return to the requested page

After an app restart the Telegram client exists but the user is not
loaded, so pages rendered first and only then (on first render) the
layout noticed the missing user and hard-redirected to the login page,
losing the URL the user was visiting; after authenticating, login
always landed on /fetchdata.

Resolve the session in the layouts' OnInitializedAsync, before any page
content renders: the body is gated behind the check (page components
are not instantiated until it completes), a silent session restore via
checkAuth(null) is attempted first, and only when interactive login is
really needed does the guard bounce to the login page, passing the
requested URL as ?returnUrl. The login page navigates back to that URL
(local paths only, to avoid open redirects) after a successful login.
ConfigLayout gets the same guard; the old after-render check is
removed.

* feat: QR code login option in the web login page

The QR login plumbing (CallQrGenerator wrapping LoginWithQRCode, QR
rendering in Index) existed but nothing invoked it, and it could not
have worked from the web anyway: when the account has 2FA,
LoginWithQRCode asks for the password through Config("password"), which
with the convenience constructor prompts on the console - QR login only
worked when driving the library from a terminal.

Wire it end to end:
- The main client is now built with a custom config callback that
  routes the "password" request to the web UI: a QrPasswordNeeded event
  switches the login form to the 2FA password step and the value is
  handed back to the waiting login task, which blocks on a
  TaskCompletionSource (5 minute timeout) on its background thread.
- The login page offers "Log in with QR code" next to the phone step:
  it shows the tg://login QR (auto-refreshed by the library when each
  token expires), instructions and a back button; on success the shared
  post-login initialization runs and navigation proceeds as with the
  normal flow.
- DoLogin's post-login tail is extracted into CompleteLogin, reused by
  the QR flow.
- checkAuth: when a session exists but no user data file is saved (the
  QR case - there is no phone to save), attempt a silent session
  restore via DoLogin(null) instead of always bouncing to the phone
  step, so QR sessions survive app restarts.

* feat: live refresh in the download/upload/task info modals

The info modals held a live reference to their model but only rendered
a snapshot taken when opened, so progress, state, duration and speed
froze until the modal was reopened.

Subscribe each modal to the aggregated throttled TransactionsChanged
event while it is visible (subscribed on ShowModal, unsubscribed on
HideModal and Dispose) and re-render on the UI thread, following the
same pattern the transfer tables use.

* Bump version to 3.7.0

---------

Co-authored-by: Mateo <mateof@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant